package cn.imvc.app.models;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import cn.imvc.app.domains.FundFlowType;
import cn.imvc.app.domains.OrderState;
import cn.imvc.app.entities.Address;
import cn.imvc.app.entities.Area;
import cn.imvc.app.entities.Commission;
import cn.imvc.app.entities.Express;
import cn.imvc.app.entities.ExpressFee;
import cn.imvc.app.entities.GoodsOrder;
import cn.imvc.app.entities.GoodsSku;
import cn.imvc.app.entities.Orders;
import cn.imvc.app.entities.StorageOrder;
import cn.imvc.app.jobs.AutomaticOrdersFinishQuarz;
import cn.imvc.app.repositories.GoodsSkuRepository;
import cn.imvc.app.repositories.OrdersRepository;
import cn.imvc.data.domain.ViewRequest;
import cn.imvc.data.util.JpqlUtil;
import cn.imvc.data.util.PageableUtil;
import cn.imvc.service.api.GoodsService;

@Service("ordersModel")
public class OrdersModelImpl implements OrdersModel {
	private static final Logger log = Logger.getLogger(OrdersModelImpl.class);
	@Autowired
	OrdersRepository ordersRepository;
	@Autowired
	StorageOrderModel storageOrderModel;
	@Autowired
	GoodsOrderModel goodsOrderModel;
	@Autowired
	GoodsService goodsService; 			//dubbo公共接口
	@Autowired
	AccountModel accountModel;
	@Autowired
	ExpressModel expressModel;
	@Autowired
	ExpressFeeModel expressFeeModel;
	@Autowired
	GoodsSkuRepository goodsSkuRepository;
	@Autowired
	FundFlowModel fundFlowModel;
	@Autowired
	CommissionModel commissionModel;
	@Autowired
	private DealerModel dealerModel;

	public LinkedHashMap<RequestMatcher, Collection<ConfigAttribute>> getRequestMap() {
		// TODO Auto-generated method stub
		return null;
	}

	public Page<Orders> find(ViewRequest viewRequest) {
		if(viewRequest == null){
			viewRequest=new ViewRequest();
		}
		viewRequest.getSort().put("o.createdAt", "desc");
		return ordersRepository.findAll("o","Orders o",viewRequest);
	}

	public Orders get(int id) {
		return ordersRepository.findOne(id);
	}

	public Orders save(Orders orders) {
		// TODO Auto-generated method stub
		return null;
	}

	public Orders update(Orders orders) {

		//如果订单到完成状态的时候，需要返还积分
		if(orders.getState() == OrderState.Finish.getValue()){
			Integer point = (orders.getResultAmount()-orders.getRefundAmount()-orders.getFreightAmount())/100;
			if(orders.getMember().getDealer().getDealerLevel().getLevel() != 1){
				point = point * 2;
			}
			accountModel.retrySaveAccount(orders.getMember().getAccount().getId(), 0, 0, point);
			log.info("订单id：："+orders.getId()+"-----accountId："+orders.getMember().getAccount().getId()+"增加积分："+point);
			fundFlowModel.saveFundFlow(orders.getMember(),point , orders.getCode(), FundFlowType.PointsUp.getValue());
			//分佣表插入
			log.info("订单id：："+orders.getId()+"-----流水更新成功!");
			Commission commOne = new Commission();
			commOne.setMember(orders.getMember());
			commOne.setAccount(orders.getMember().getAccount());
			commOne.setType((byte) 10);
			commOne.setReferId(orders.getId());
			commOne.setReferCode(orders.getCode());
			commOne.setMoney(point);
			commOne.setRate(100);
			commOne.setCreatedAt(new Date());
			commOne.setFromMember(orders.getMember());
			commissionModel.save(commOne);
			log.info("订单id：："+orders.getId()+"-----分佣更新成功!");
			
			commissionModel.updateOrdersCommission(orders);
			
			if (orders.getMember().getDealer().getDealerLevel().getLevel() == 1) {
				log.info("1级订单id:::"+orders.getId()+"用户判断是否升级为经销商。");
				dealerModel.updatePointAuto(orders.getMember().getDealer());
			}
		}
		return ordersRepository.saveAndFlush(orders);
	}

	public void delete(int id) {
		// TODO Auto-generated method stub

	}

	public void deleteBatch(int[] ids) {
		// TODO Auto-generated method stub

	}

	public Page<Orders> findByState(Byte state,Integer memberId,PageRequest pageRequest){
//		return ordersRepository.findByState((byte)state,state==0,memberId, pageRequest);
		return null;
	}

	/**
	 * 驳回订单，反库存，返钱宝金额
	 */
	public boolean updateAdminCancel(int id) {
		Orders orders = get(id);
		if(orders.getState() != OrderState.PaySuccess.getValue()){
			return false;
		}
		return updateCancel(orders);
	}

	public List<Orders> findByState(Byte state) {
		return ordersRepository.findAllByState(state);
	}

	public void updateRefundAmount(int refundAmount, int id) {
		ordersRepository.updateRefundAmountById(refundAmount, id);
	}

	//
	public Orders updateConfirm(int id) {
		Orders orders = get(id);
		if(orders.getState() == OrderState.PaySuccess.getValue()){
			orders.setState(OrderState.Delivery.getValue());
			//同步下级订单和三级订单()
			storageOrderModel.updateSyncStateByPreState(orders,OrderState.PaySuccess.getValue());
			orders = ordersRepository.saveAndFlush(orders);
			return orders;
		}
		return null;
	}

	public void updateBatchConfirm(int[] ids) {
		for (int id : ids) {
			updateConfirm(id);
		}
	}

	
	public Page<Object[]> findPageObjects(ViewRequest viewRequest) {
		if(viewRequest == null){
			viewRequest=new ViewRequest();
		}
		String sql = "select o.id,o.code,o.paycode,o.paychannel,o.payTime,o.createdAt,o.goodsAmount,o.discountAmount,o.orderAmount"
				+ ",o.freightAmount,o.resultAmount,o.refundAmount,o.finishTime,max(g.state),m.name from orders o left join goods_order g on o.id = g.orderId left join member m on o.memberId=m.id "
				+ JpqlUtil.toWhere(viewRequest.getParams()) + " group by o.id,o.code,o.paycode,o.paychannel,o.payTime,o.createdAt,o.goodsAmount,o.discountAmount,o.orderAmount"
				+ ",o.freightAmount,o.resultAmount,o.refundAmount,o.finishTime,m.name order by o.createdAt desc";
		return ordersRepository.findAllSql(sql, PageableUtil.toPageable(viewRequest));
	}

	/**
	 * 强制驳回该订单，用于订单确认状态时，缺货或其他原因，将该订单强制修改为驳回状态
	 */
	public boolean updateDoCancel(int id) {
		Orders orders = get(id);
		if(orders.getState() != OrderState.Delivery.getValue()){
			return false;
		}
		return updateCancel(orders);
	}
	
	private boolean updateCancel(Orders orders){
		orders.setState(OrderState.AdminCancel.getValue());
		orders.setRefundAmount(orders.getResultAmount());
		update(orders);
		storageOrderModel.updateSyncState(orders);
		
		List<Integer[]> skuInfos = new ArrayList<Integer[]>();
			//返还库存
		Iterator<GoodsOrder> iterator = orders.getGoodsOrder().iterator();
		while (iterator.hasNext()) {
			Integer[] skuInfo = new Integer[3];
			GoodsOrder goodsOrder = (GoodsOrder) iterator.next();
			skuInfo[0] = goodsOrder.getGoodsSku().getId();
			skuInfo[1] = -goodsOrder.getNum();
			skuInfo[2] = goodsOrder.getId();
			skuInfos.add(skuInfo);
		}
		goodsService.retryBatchUpdateGoodsInventory(skuInfos, false);
		//5、退款操作
		accountModel.retryRefundAccount(orders.getMember().getId(), orders.getRefundAmount(), FundFlowType.Refund.getValue(),String.valueOf(orders.getCode()));		
		
		return true;
	}
	
	/**
	 * 计算运费
	 * 
	 * @param generateOrderInfo
	 * @param address
	 * @param storageId
	 *            若不为null，则计算该仓库运费；否则计算所有仓库运费并返回最大值
	 * @return Integer[首重运费,超重运费,总运费,总重量]
	 */
	public Integer calculateFreightAmount(Orders order) {
		Integer freightAmount = 0;
		Integer weightAmount = 0;
		Set<StorageOrder> storageOrderList = order.getStorageOrder();
		Assert.notEmpty(storageOrderList);
		for (StorageOrder temStorageOrder : storageOrderList) {
			Set<GoodsOrder> goodsOrderSet = temStorageOrder.getGoodsOrder();
			List<GoodsOrder> goodsOrders = goodsOrderSet.stream().filter(g -> g.getState() == OrderState.Delivery.getValue()).collect(Collectors.toList());
			List<GoodsSku> goodsSkuList = goodsSkuRepository.findByIdIn(goodsOrders.stream().mapToInt((i) -> i.getGoodsSku().getId()).toArray());
			weightAmount = goodsSkuList.stream().filter(sku -> sku.getGoods().getIsFreight() != null && sku.getGoods().getIsFreight() == 1)
					.mapToInt((sku) -> sku.getWeight() == null ? 0 : sku.getWeight()).sum();
			Integer temFreightAmount = doCalculate(temStorageOrder, order.getAddressId(),weightAmount);
			freightAmount = temFreightAmount > freightAmount ? temFreightAmount : freightAmount;
		}
		return freightAmount;
	}
	
	private Integer doCalculate(StorageOrder storageOrder, String addressIds, Integer orderWeight) {
		Set<GoodsOrder> goodsOrderSet = storageOrder.getGoodsOrder();
		List<GoodsOrder> goodsOrders = goodsOrderSet.stream().filter(g -> g.getState() == OrderState.Delivery.getValue()).collect(Collectors.toList());
		if(goodsOrders.isEmpty()){
			return 0;
		}
		String[] addressIdArray = addressIds.split(",");
		// 运费 先查询 express_fee 若无对应记录则查询express
		Integer expressId = storageOrder.getExpress().getId();
		Express express = expressModel.get(expressId);
		ExpressFee expressFee = null;
		if(addressIdArray.length == 3){
			int countryId = Integer.valueOf(addressIdArray[2]);
			if (expressFee == null) {
				expressFee = expressFeeModel.findByExpressAndAreaId(express, countryId);
			}
		}
		if(addressIdArray.length == 2){
			int cityId = Integer.valueOf(addressIdArray[1]);
			if (expressFee == null) {
				expressFee = expressFeeModel.findByExpressAndAreaId(express, cityId);
				// areaIds.add(city.getId());
			}
		}
		if(addressIdArray.length == 1){
			int provinceId = Integer.valueOf(addressIdArray[0]);
			if (expressFee == null) {
				expressFee = expressFeeModel.findByExpressAndAreaId(express, provinceId);
			}
		}
		Integer first = express.getFirst();
		Integer firstPrice = express.getFirstPrice();
		Integer ad = express.getAdditional();
		Integer adPrice = express.getAdditionalPrice();
		if (expressFee != null) {// 有值，按此计算运费
			first = expressFee.getFirst();
			firstPrice = expressFee.getFirstPrice();
			ad = expressFee.getAdditional();
			adPrice = expressFee.getAdditionalPrice();
		} else {// 按express计算
		}
		List<GoodsSku> goodsSkuList = goodsSkuRepository.findByIdIn(goodsOrders.stream().mapToInt((i) -> i.getGoodsSku().getId()).toArray());
		Assert.isTrue(goodsOrders.size() == goodsSkuList.size(), "size must be equal");
		Integer orderGoodsWithoutGiftSize = goodsSkuList.stream().filter(sku -> sku.getGoods().getIsFreight() != null && sku.getGoods().getIsFreight() == 1)
				.toArray().length;
		Integer weightAmount = goodsSkuList.stream().filter(sku->sku.getGoods().getIsFreight()!=null&&sku.getGoods().getIsFreight()==1).mapToInt((sku) -> sku.getWeight() == null ? 0 : sku.getWeight()).sum();
		if (orderWeight == null) {
			orderWeight = weightAmount;
		}
		Integer additionalCost = 0;
		if (weightAmount > first) {
//			additionalCost = ((weightAmount - first) + ad - 1) / ad * asPrice;
			if(ad == 0){
				additionalCost = 0;
			}else{
				additionalCost = ((orderWeight - first) + ad - 1) / ad * adPrice;
			}
		}
		if (orderGoodsWithoutGiftSize == 0){
			// 如果单子只有一个免运费的商品
			return 0;
		}else{
			return firstPrice + additionalCost;
		}
	}
	
	
	//系统取消订单
	@Override
	public void updateSystemCancelOrder(Orders orders) {
		Orders order = get(orders.getId());
		order.setCancelTime(new Date());
		order.setState(OrderState.SystemCancel.getValue());
		
		update(order);
		storageOrderModel.updateSyncState(order);
		
		List<Integer[]> skuInfos = new ArrayList<Integer[]>();
			//返还库存
		Iterator<GoodsOrder> iterator = order.getGoodsOrder().iterator();
		while (iterator.hasNext()) {
			Integer[] skuInfo = new Integer[3];
			GoodsOrder goodsOrder = (GoodsOrder) iterator.next();
			skuInfo[0] = goodsOrder.getGoodsSku().getId();
			skuInfo[1] = -goodsOrder.getNum();
			skuInfo[2] = goodsOrder.getId();
			skuInfos.add(skuInfo);
		}
		goodsService.retryBatchUpdateGoodsInventory(skuInfos, false);
	}
	
	public boolean checkSameOrderState(int orderId,Byte state){
		Orders orders = ordersRepository.findOne(orderId);
		if(orders != null && orders.getState() == state){
			Iterator<StorageOrder> iterator = orders.getStorageOrder().iterator();
			while (iterator.hasNext()) {
				StorageOrder storageOrder = (StorageOrder) iterator.next();
				if(storageOrder.getState() == state){
					Iterator<GoodsOrder> goodsOrderIterator = storageOrder.getGoodsOrder().iterator();
					while (goodsOrderIterator.hasNext()) {
						GoodsOrder goodsOrder = (GoodsOrder) goodsOrderIterator.next();
						if(goodsOrder.getState() != state){
							return false;
						}
					}
				}else{
					return false;
				}
			}
		}else{
			return false;
		}
		return true;
	}

	@Override
	public List<Orders> findByStateAndCreatedAtLessThan(Byte value, Date date) {
		return ordersRepository.findByStateAndCreatedAtLessThan(value,date);
	}
}
